#include "gtest/gtest.h"
#include "pistache/Uri.h"
using namespace Pistache;

TEST(accessor_tests, authority_string)
{
	Uri u("http://testname.com:81/path?baz");
	Uri a = u.authority();

	ASSERT_EQ("/path", u.path());
	ASSERT_EQ("http", a.scheme());
	ASSERT_EQ("testname.com", a.host());
	ASSERT_EQ(81, a.port());
	ASSERT_EQ(Uri("http://testname.com:81"), a);
}

TEST(combining_tests, append_path)
{
	std::string uri_str = "http://testname.com/path?baz";
	UriBuilder ub(uri_str);
	Uri combined = ub.append_path("/baz").to_uri();
	ASSERT_EQ(Uri("http://testname.com/path/baz?baz"), combined);
}

TEST(combining_tests, append_empty_path)
{
	std::string uri_str("http://fakeuri.net");
	Uri u = Uri(uri_str);
	UriBuilder ub(u);
	Uri combined = ub.append_path("").to_uri();
	ASSERT_EQ(u, combined);
}

TEST(combining_tests, append_query)
{
	std::string uri_str(("http://testname.com/path1?key1=value2"));
	UriBuilder ub(uri_str);
	Uri combined = ub.append_query(Uri(("http://testname2.com/path2?key2=value3")).query()).to_uri();

	ASSERT_EQ(("http://testname.com/path1?key1=value2&key2=value3"), combined.to_string());
}

TEST(combining_tests, append_empty_query)
{
	std::string uri_str(("http://fakeuri.org/?key=value"));
	Uri u(uri_str);
	UriBuilder ub(u);
	Uri combined = ub.append_query(("")).to_uri();

	ASSERT_EQ(u, combined);
}

TEST(combining_tests, append)
{
	std::string uri_str(("http://testname.com/path1?key1=value2"));
	UriBuilder ub(uri_str);
	Uri combined = ub.append(Uri("http://testname2.com/path2?key2=value3")).to_uri();

	ASSERT_EQ(("http://testname.com/path1/path2?key1=value2&key2=value3"), combined.to_string());
	ASSERT_EQ(("/path1/path2?key1=value2&key2=value3"), combined.resource().to_string());
}

TEST(combining_tests, append_empty)
{
	std::string uri_str(("http://myhost.com"));
	Uri u(uri_str);
	UriBuilder ub(u);
	Uri combined = ub.append(Uri("")).to_uri();

	ASSERT_EQ(u, combined);
}

TEST(constructor_tests, parsing_constructor_char)
{
	Uri u(Uri::encode_uri(("net.tcp://steve:@testname.com:81/bleh%?qstring#goo")));

	ASSERT_EQ(("net.tcp"), u.scheme());
	ASSERT_EQ(("steve:"), u.user_info());
	ASSERT_EQ(("testname.com"), u.host());
	ASSERT_EQ(81, u.port());
	ASSERT_EQ(("/bleh%25"), u.path());
	ASSERT_EQ(("qstring"), u.query());
	ASSERT_EQ(("goo"), u.fragment());
}

TEST(constructor_tests, parsing_constructor_encoded_string)
{
	Uri u(Uri::encode_uri(("net.tcp://testname.com:81/bleh%?qstring#goo")));

	ASSERT_EQ(("net.tcp"), u.scheme());
	ASSERT_EQ(("testname.com"), u.host());
	ASSERT_EQ(81, u.port());
	ASSERT_EQ(("/bleh%25"), u.path());
	ASSERT_EQ(("qstring"), u.query());
	ASSERT_EQ(("goo"), u.fragment());
}

TEST(constructor_tests, parsing_constructor_string_string)
{
	Uri u(Uri::encode_uri(("net.tcp://testname.com:81/bleh%?qstring#goo")));

	ASSERT_EQ(("net.tcp"), u.scheme());
	ASSERT_EQ(("testname.com"), u.host());
	ASSERT_EQ(81, u.port());
	ASSERT_EQ(("/bleh%25"), u.path());
	ASSERT_EQ(("qstring"), u.query());
	ASSERT_EQ(("goo"), u.fragment());
}

TEST(constructor_tests, empty_strings)
{
	ASSERT_TRUE(Uri(("")).is_empty());
	ASSERT_TRUE(Uri(("")).is_empty());
	ASSERT_TRUE(Uri(Uri::encode_uri((""))).is_empty());
}

TEST(constructor_tests, default_constructor) { ASSERT_TRUE(Uri().is_empty()); }

TEST(constructor_tests, relative_ref_string)
{
	Uri u(Uri::encode_uri(("first/second#boff")));

	ASSERT_EQ((""), u.scheme());
	ASSERT_EQ((""), u.host());
	ASSERT_EQ(0, u.port());
	ASSERT_EQ(("first/second"), u.path());
	ASSERT_EQ((""), u.query());
	ASSERT_EQ(("boff"), u.fragment());
}

TEST(constructor_tests, absolute_ref_string)
{
	Uri u(Uri::encode_uri(("/first/second#boff")));

	ASSERT_EQ((""), u.scheme());
	ASSERT_EQ((""), u.host());
	ASSERT_EQ(0, u.port());
	ASSERT_EQ(("/first/second"), u.path());
	ASSERT_EQ((""), u.query());
	ASSERT_EQ(("boff"), u.fragment());
}

TEST(constructor_tests, copy_constructor)
{
	Uri original(("http://st:pass@localhost:456/path1?qstring#goo"));
	Uri new_uri(original);

	ASSERT_EQ(original, new_uri);
}

TEST(constructor_tests, move_constructor)
{
	const std::string uri_str(("http://localhost:456/path1?qstring#goo"));
	Uri original(uri_str);
	Uri new_uri = std::move(original);

	ASSERT_EQ(uri_str, new_uri.to_string());
	ASSERT_EQ(Uri(uri_str), new_uri);
}

TEST(constructor_tests, assignment_operator)
{
	Uri original(("http://localhost:456/path?qstring#goo"));
	Uri new_uri = original;

	ASSERT_EQ(original, new_uri);
}

// Tests invalid URI being passed in constructor.
TEST(constructor_tests, parsing_constructor_invalid)
{
	ASSERT_THROW(Uri(("123http://localhost:345/")), UriException);
	ASSERT_THROW(Uri(("h*ttp://localhost:345/")), UriException);
	ASSERT_THROW(Uri(("http://localhost:345/\"")), UriException);
	ASSERT_THROW(Uri(("http://localhost:345/path?\"")), UriException);
	ASSERT_THROW(Uri(("http://local\"host:345/")), UriException);
}

// Tests a variety of different URIs using the examples in RFC 2732
TEST(constructor_tests, RFC_2732_examples_string)
{
	// The URI parser will make characters lower case
	Uri http1(("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html"));
	ASSERT_EQ(("http"), http1.scheme());
	ASSERT_EQ(("[fedc:ba98:7654:3210:fedc:ba98:7654:3210]"), http1.host());
	ASSERT_EQ(80, http1.port());
	ASSERT_EQ(("/index.html"), http1.path());
	ASSERT_EQ((""), http1.query());

	Uri http2(("http://[1080:0:0:0:8:800:200C:417A]/index.html"));
	ASSERT_EQ(("http"), http2.scheme());
	ASSERT_EQ(("[1080:0:0:0:8:800:200c:417a]"), http2.host());
	ASSERT_EQ(0, http2.port());
	ASSERT_EQ(("/index.html"), http2.path());
	ASSERT_EQ((""), http2.query());

	Uri http3(("https://[3ffe:2a00:100:7031::1]"));
	ASSERT_EQ(("https"), http3.scheme());
	ASSERT_EQ(("[3ffe:2a00:100:7031::1]"), http3.host());
	ASSERT_EQ(0, http3.port());
	ASSERT_EQ(("/"), http3.path());
	ASSERT_EQ((""), http3.query());

	Uri http4(("http://[::192.9.5.5]/ipng"));
	ASSERT_EQ(("http"), http4.scheme());
	ASSERT_EQ(("[::192.9.5.5]"), http4.host());
	ASSERT_EQ(0, http4.port());
	ASSERT_EQ(("/ipng"), http4.path());
	ASSERT_EQ((""), http4.query());

	Uri http5(("http://[1080::8:800:200C:417A]/foo"));
	ASSERT_EQ(("http"), http5.scheme());
	ASSERT_EQ(("[1080::8:800:200c:417a]"), http5.host());
	ASSERT_EQ(0, http5.port());
	ASSERT_EQ(("/foo"), http5.path());
	ASSERT_EQ((""), http5.query());

	Uri http6(("http://[::FFFF:129.144.52.38]:80/index.html"));
	ASSERT_EQ(("http"), http6.scheme());
	ASSERT_EQ(("[::ffff:129.144.52.38]"), http6.host());
	ASSERT_EQ(80, http6.port());
	ASSERT_EQ(("/index.html"), http6.path());
	ASSERT_EQ((""), http6.query());

	Uri http7(("http://[2010:836B:4179::836B:4179]"));
	ASSERT_EQ(("http"), http7.scheme());
	ASSERT_EQ(("[2010:836b:4179::836b:4179]"), http7.host());
	ASSERT_EQ(0, http7.port());
	ASSERT_EQ(("/"), http7.path());
	ASSERT_EQ((""), http7.query());
}

// Tests a variety of different URIs using the examples in RFC 3986.
TEST(constructor_tests, RFC_3968_examples_string)
{
	Uri ftp(("ftp://ftp.is.co.za/rfc/rfc1808.txt"));
	ASSERT_EQ(("ftp"), ftp.scheme());
	ASSERT_EQ((""), ftp.user_info());
	ASSERT_EQ(("ftp.is.co.za"), ftp.host());
	ASSERT_EQ(0, ftp.port());
	ASSERT_EQ(("/rfc/rfc1808.txt"), ftp.path());
	ASSERT_EQ((""), ftp.query());
	ASSERT_EQ((""), ftp.fragment());

	// TFS #371892
	// Uri ldap(("ldap://[2001:db8::7]/?c=GB#objectClass?one"));
	// ASSERT_EQ(("ldap"), ldap.scheme());
	// ASSERT_EQ((""), ldap.user_info());
	// ASSERT_EQ(("2001:db8::7"), ldap.host());
	// ASSERT_EQ(0, ldap.port());
	// ASSERT_EQ(("/"), ldap.path());
	// ASSERT_EQ(("c=GB"), ldap.query());
	// ASSERT_EQ(("objectClass?one"), ldap.fragment());

	// We don't support anything scheme specific like in C# so
	// these common ones don't have a great experience yet.
	Uri mailto(("mailto:John.Doe@example.com"));
	ASSERT_EQ(("mailto"), mailto.scheme());
	ASSERT_EQ((""), mailto.user_info());
	ASSERT_EQ((""), mailto.host());
	ASSERT_EQ(0, mailto.port());
	ASSERT_EQ(("John.Doe@example.com"), mailto.path());
	ASSERT_EQ((""), mailto.query());
	ASSERT_EQ((""), mailto.fragment());

	Uri tel(("tel:+1-816-555-1212"));
	ASSERT_EQ(("tel"), tel.scheme());
	ASSERT_EQ((""), tel.user_info());
	ASSERT_EQ((""), tel.host());
	ASSERT_EQ(0, tel.port());
	ASSERT_EQ(("+1-816-555-1212"), tel.path());
	ASSERT_EQ((""), tel.query());
	ASSERT_EQ((""), tel.fragment());

	Uri telnet(("telnet://192.0.2.16:80/"));
	ASSERT_EQ(("telnet"), telnet.scheme());
	ASSERT_EQ((""), telnet.user_info());
	ASSERT_EQ(("192.0.2.16"), telnet.host());
	ASSERT_EQ(80, telnet.port());
	ASSERT_EQ(("/"), telnet.path());
	ASSERT_EQ((""), telnet.query());
	ASSERT_EQ((""), telnet.fragment());
}

TEST(constructor_tests, user_info_string)
{
	Uri ftp(("ftp://johndoe:testname@ftp.is.co.za/rfc/rfc1808.txt"));
	ASSERT_EQ(("ftp"), ftp.scheme());
	ASSERT_EQ(("johndoe:testname"), ftp.user_info());
	ASSERT_EQ(("ftp.is.co.za"), ftp.host());
	ASSERT_EQ(0, ftp.port());
	ASSERT_EQ(("/rfc/rfc1808.txt"), ftp.path());
	ASSERT_EQ((""), ftp.query());
	ASSERT_EQ((""), ftp.fragment());
}

// Test query component can be separated with '&' or ';'.
TEST(constructor_tests, query_seperated_with_semi_colon)
{
	Uri u(("http://localhost/path1?key1=val1;key2=val2"));
	ASSERT_EQ(("key1=val1;key2=val2"), u.query());
}

TEST(conversions_tests, to_string_conversion)
{
	std::string encoded = Uri::encode_uri(("http://testname.com/%%?qstring"));
	Uri u1(("http://testname.com/%25%25?qstring"));

	ASSERT_EQ(Uri::decode(encoded), Uri::decode(u1.to_string()));
}

TEST(conversions_tests, to_encoded_string)
{
	std::string  encoded = Uri::encode_uri(("http://testname.com/%%?qstring"));
	Uri u(("http://testname.com/%25%25?qstring"));

	ASSERT_EQ(encoded, u.to_string());
}

TEST(conversions_tests, empty_to_string)
{
	Uri u;
	ASSERT_EQ(("/"), u.to_string());
}

TEST(diagnostic_tests, empty_components)
{
	ASSERT_TRUE(Uri().is_empty());

	ASSERT_FALSE(Uri().is_authority());

	ASSERT_FALSE(Uri().is_host_loopback());
	ASSERT_FALSE(Uri().is_host_wildcard());
	ASSERT_FALSE(Uri().is_host_portable());

	ASSERT_FALSE(Uri().is_port_default());
}

TEST(diagnostic_tests, is_authority)
{
	ASSERT_TRUE(Uri(("http://first.second/")).is_authority());
	ASSERT_TRUE(Uri(("http://first.second")).is_authority());

	ASSERT_FALSE(Uri(("http://first.second/b")).is_authority());
	ASSERT_FALSE(Uri(("http://first.second?qstring")).is_authority());
	ASSERT_FALSE(Uri(("http://first.second#third")).is_authority());
}

TEST(diagnostic_tests, has_same_authority)
{
	ASSERT_TRUE(Uri(("http://first.second/")).has_same_authority(Uri(("http://first.second/path"))));
	ASSERT_TRUE(Uri(("http://first.second:83/")).has_same_authority(Uri(("http://first.second:83/path:83"))));

	ASSERT_FALSE(Uri(("http://first.second:82/")).has_same_authority(Uri(("http://first.second/path"))));
	ASSERT_FALSE(Uri(("tcp://first.second:82/")).has_same_authority(Uri(("http://first.second/path"))));
	ASSERT_FALSE(Uri(("http://path.:82/")).has_same_authority(Uri(("http://first.second/path"))));
}

TEST(diagnostic_tests, has_same_authority_empty)
{
	ASSERT_FALSE(Uri().has_same_authority(Uri()));
	ASSERT_FALSE(Uri(("http://first.second/")).has_same_authority(Uri()));
	ASSERT_FALSE(Uri().has_same_authority(Uri(("http://first.second/"))));
}

TEST(diagnostic_tests, is_host_wildcard)
{
	ASSERT_TRUE(Uri(("http://*/")).is_host_wildcard());
	ASSERT_TRUE(Uri(("http://+/?qstring")).is_host_wildcard());

	ASSERT_FALSE(Uri(("http://bleh/?qstring")).is_host_wildcard());
	ASSERT_FALSE(Uri(("http://+*/?qstring")).is_host_wildcard());
}

TEST(diagnostic_tests, is_host_loopback)
{
	ASSERT_TRUE(Uri(("http://localhost/")).is_host_loopback());
	ASSERT_TRUE(Uri(("http://LoCALHoST/")).is_host_loopback());

	ASSERT_FALSE(Uri(("http://127")).is_host_loopback());
	ASSERT_FALSE(Uri(("http://bleh/?qstring")).is_host_loopback());
	ASSERT_FALSE(Uri(("http://+*/?qstring")).is_host_loopback());
	ASSERT_TRUE(Uri(("http://127.0.0.1/")).is_host_loopback());
	ASSERT_TRUE(Uri(("http://127.155.0.1/")).is_host_loopback());
	ASSERT_FALSE(Uri(("http://128.0.0.1/")).is_host_loopback());
}

TEST(diagnostic_tests, is_host_portable)
{
	ASSERT_TRUE(Uri(("http://bleh/?qstring")).is_host_portable());

	ASSERT_FALSE(Uri(("http://localhost/")).is_host_portable());
	ASSERT_FALSE(Uri(("http://+/?qstring")).is_host_portable());
}

TEST(diagnostic_tests, is_port_default)
{
	ASSERT_TRUE(Uri(("http://bleh/?qstring")).is_port_default());
	ASSERT_TRUE(Uri(("http://localhost:0/")).is_port_default());

	ASSERT_FALSE(Uri(("http://+:85/?qstring")).is_port_default());
}

TEST(diagnostic_tests, is_path_empty)
{
	ASSERT_TRUE(Uri(("http://bleh/?qstring")).is_path_empty());
	ASSERT_TRUE(Uri(("http://localhost:0")).is_path_empty());

	ASSERT_FALSE(Uri(("http://+:85/path/?qstring")).is_path_empty());
}

#ifdef _WIN32
#pragma warning(push)
#pragma warning(disable : 4428)
TEST(encoding_tests, encode_string)
{
	std::string result = Uri::encode_uri("first%second\u4e2d\u56fd");
	ASSERT_EQ(("first%25second%E4%B8%AD%E5%9B%BD"), result);

	result = Uri::encode_uri(("first%second"));
	ASSERT_EQ(("first%25second"), result);
}

TEST(encoding_tests, decode_string)
{
	std::string result = Uri::decode(("first%25second%E4%B8%AD%E5%9B%BD"));
	ASSERT_EQ("first%second\u4e2d\u56fd", result);

	result = Uri::decode(("first%25second"));
	ASSERT_EQ(("first%second"), result);
}
#pragma warning(pop)
#endif

TEST(encoding_tests, encode_characters_in_resource)
{
	std::string result = Uri::encode_uri(("http://path%name/%#!%"));
	ASSERT_EQ(("http://path%25name/%25#!%25"), result);
}

// Tests trying to encode empty strings.
TEST(encoding_tests, encode_decode_empty_strings)
{
	// std::string
	std::string result = Uri::encode_uri((""));
	ASSERT_EQ((""), result);
	std::string str = Uri::decode(result);
	ASSERT_EQ((""), str);

	// std::wstring
	result = Uri::encode_uri((""));
	ASSERT_EQ((""), result);
	auto wstr = Uri::decode(result);
	ASSERT_EQ((""), wstr);
}

// Tests encoding in various components of the URI.
TEST(encoding_tests, encode_uri_multiple_components)
{
	// only encodes characters that aren't in the unreserved and reserved set.

	// std::string
	std::string str(("htt p://^localhost:80/path ?^one=two# frag"));
	std::string result = Uri::encode_uri(str);
	ASSERT_EQ(("htt%20p://%5Elocalhost:80/path%20?%5Eone=two#%20frag"), result);
	ASSERT_EQ(str, Uri::decode(result));
}

// Tests encoding individual components of a URI.
TEST(encoding_tests, encode_uri_component)
{
	// encodes all characters not in the unreserved set.

	// std::string
	std::string str(("path with^spaced"));
	std::string result = Uri::encode_uri(str);
	ASSERT_EQ(("path%20with%5Espaced"), result);
	ASSERT_EQ(str, Uri::decode(result));
}

// Tests trying to decode a string that doesn't have 2 hex digits after %
TEST(encoding_tests, decode_invalid_hex)
{
	ASSERT_THROW(Uri::decode(("hehe%")), UriException);
	ASSERT_THROW(Uri::decode(("hehe%2")), UriException);
	ASSERT_THROW(Uri::decode(("hehe%4H")), UriException);
	ASSERT_THROW(Uri::decode(("he%kkhe")), UriException);
}

// Tests making sure '+' is encoded even though nonstandard, so it doesn't
// get mistaken later by some implementations as a space.
TEST(encoding_tests, encode_plus_char)
{
	const std::string encodedPlus(("%2B"));

	UriBuilder builder;
	builder.set_user_info(("+"), true);
	builder.set_path(("+"), true);
	builder.set_query(("+"), true);
	builder.set_fragment(("+"), true);

	ASSERT_EQ(builder.user_info(), encodedPlus);
	ASSERT_EQ(builder.path(), encodedPlus);
	ASSERT_EQ(builder.query(), encodedPlus);
	ASSERT_EQ(builder.fragment(), encodedPlus);
}

TEST(encoding_tests, bug_417601)
{
	std::ostringstream ss1;
	auto enc1 = Uri::encode_data_string(("!"));
	ss1 << enc1;

	ASSERT_EQ(("%21"), ss1.str());
}

TEST(operator_tests, uri_basic_equality)
{
	ASSERT_EQ(Uri(("")), Uri(("")));

	Uri u1(("http://localhost:80/path1"));
	Uri u2(("http://localhost:80/path1"));
	ASSERT_EQ(u1, u2);
}

TEST(operator_tests, uri_decoded_equality)
{
	UriBuilder u3b(("http://localhost:80"));
	u3b.set_path(("path 1"), true);

	Uri u3 = u3b.to_uri();
	Uri u4(("http://localhost:80/path%201"));
	ASSERT_EQ(u3, u4);

	Uri u5(("http://localhost:80/pat%68a1"));
	Uri u6(("http://localhost:80/patha1"));
	ASSERT_EQ(u5, u6);

	Uri u9(("http://localhost:80/patha1?name=first#t%65st"));
	Uri u10(("http://localhost:80/patha1?name=first#test"));
	ASSERT_EQ(u9, u10);
}

TEST(operator_tests, uri_basic_inequality)
{
	ASSERT_NE(Uri(("http://localhost:80/path1")), Uri(("https://localhost:80/path1")));
	ASSERT_NE(Uri(("http://localhost:80/path1")), Uri(("http://localhost2:80/path1")));
	ASSERT_NE(Uri(("http://localhost:80/path1")), Uri(("http://localhost:81/path1")));
	ASSERT_NE(Uri(("http://localhost:80/path1")), Uri(("http://localhost:80/path2")));
	ASSERT_NE(Uri(("http://localhost:80/path1?key=value")),
		Uri(("http://localhost:80/path1?key=value2")));
	ASSERT_NE(Uri(("http://localhost:80/path1?key=value#nose")),
		Uri(("http://localhost:80/path1?key=value#nose1")));
}

TEST(operator_tests, test_empty)
{
	ASSERT_EQ(Uri(), Uri());
	ASSERT_EQ(Uri(("htTp://Path")), Uri(("hTtp://pAth")));

	ASSERT_NE(Uri(("http://path")), Uri());
	ASSERT_NE(Uri(), Uri(("http://path")));
	ASSERT_NE(Uri(("http://path1")), Uri(("http://path2")));
}

// 5.4.1. Normal Examples https://tools.ietf.org/html/rfc3986#section-5.4.1
TEST(resolve_uri_tests, resolve_uri_normal)
{
	const Uri baseUri = Uri("http://a/b/c/d;p?q");

	ASSERT_EQ(baseUri.resolve_uri(("g:h")), ("g:h"));
	ASSERT_EQ(baseUri.resolve_uri(("g")), ("http://a/b/c/g"));
	ASSERT_EQ(baseUri.resolve_uri(("./g")), ("http://a/b/c/g"));
	ASSERT_EQ(baseUri.resolve_uri(("g/")), ("http://a/b/c/g/"));
	ASSERT_EQ(baseUri.resolve_uri(("/g")), ("http://a/g"));
	ASSERT_EQ(baseUri.resolve_uri(("//g")), ("http://g"));
	ASSERT_EQ(baseUri.resolve_uri(("?y")), ("http://a/b/c/d;p?y"));
	ASSERT_EQ(baseUri.resolve_uri(("g?y")), ("http://a/b/c/g?y"));
	ASSERT_EQ(baseUri.resolve_uri(("#s")), ("http://a/b/c/d;p?q#s"));
	ASSERT_EQ(baseUri.resolve_uri(("g#s")), ("http://a/b/c/g#s"));
	ASSERT_EQ(baseUri.resolve_uri(("g?y#s")), ("http://a/b/c/g?y#s"));
	ASSERT_EQ(baseUri.resolve_uri((";x")), ("http://a/b/c/;x"));
	ASSERT_EQ(baseUri.resolve_uri(("g;x")), ("http://a/b/c/g;x"));
	ASSERT_EQ(baseUri.resolve_uri(("g;x?y#s")), ("http://a/b/c/g;x?y#s"));
	ASSERT_EQ(baseUri.resolve_uri(("")), ("http://a/b/c/d;p?q"));
	ASSERT_EQ(baseUri.resolve_uri((".")), ("http://a/b/c/"));
	ASSERT_EQ(baseUri.resolve_uri(("./")), ("http://a/b/c/"));
	ASSERT_EQ(baseUri.resolve_uri(("..")), ("http://a/b/"));
	ASSERT_EQ(baseUri.resolve_uri(("../")), ("http://a/b/"));
	ASSERT_EQ(baseUri.resolve_uri(("../g")), ("http://a/b/g"));
	ASSERT_EQ(baseUri.resolve_uri(("../..")), ("http://a/"));
	ASSERT_EQ(baseUri.resolve_uri(("../../")), ("http://a/"));
	ASSERT_EQ(baseUri.resolve_uri(("../../g")), ("http://a/g"));
}
// 5.4.2. Abnormal Examples https://tools.ietf.org/html/rfc3986#section-5.4.2
TEST(resolve_uri_tests, resolve_uri_abnormal)
{
	const Uri baseUri = Uri("http://a/b/c/d;p?q");

	ASSERT_EQ(baseUri.resolve_uri(("../../../g")), ("http://a/g"));
	ASSERT_EQ(baseUri.resolve_uri(("../../../../g")), ("http://a/g"));
	ASSERT_EQ(baseUri.resolve_uri(("/./g")), ("http://a/g"));
	ASSERT_EQ(baseUri.resolve_uri(("/../g")), ("http://a/g"));
	ASSERT_EQ(baseUri.resolve_uri(("g.")), ("http://a/b/c/g."));
	ASSERT_EQ(baseUri.resolve_uri((".g")), ("http://a/b/c/.g"));
	ASSERT_EQ(baseUri.resolve_uri(("g..")), ("http://a/b/c/g.."));
	ASSERT_EQ(baseUri.resolve_uri(("..g")), ("http://a/b/c/..g"));
	ASSERT_EQ(baseUri.resolve_uri(("./../g")), ("http://a/b/g"));
	ASSERT_EQ(baseUri.resolve_uri(("./g/.")), ("http://a/b/c/g/"));
	ASSERT_EQ(baseUri.resolve_uri(("g/./h")), ("http://a/b/c/g/h"));
	ASSERT_EQ(baseUri.resolve_uri(("g/../h")), ("http://a/b/c/h"));
	ASSERT_EQ(baseUri.resolve_uri(("g;x=1/./y")), ("http://a/b/c/g;x=1/y"));
	ASSERT_EQ(baseUri.resolve_uri(("g;x=1/../y")), ("http://a/b/c/y"));
	ASSERT_EQ(baseUri.resolve_uri(("g?y/./x")), ("http://a/b/c/g?y/./x"));
	ASSERT_EQ(baseUri.resolve_uri(("g?y/../x")), ("http://a/b/c/g?y/../x"));
	ASSERT_EQ(baseUri.resolve_uri(("g#s/./x")), ("http://a/b/c/g#s/./x"));
	ASSERT_EQ(baseUri.resolve_uri(("g#s/../x")), ("http://a/b/c/g#s/../x"));
	ASSERT_EQ(baseUri.resolve_uri(("http:g")), ("http:g"));
}

TEST(splitting_tests, split_string)
{
	std::vector<std::string> s = Uri::split_path(("/first/second/third"));
	ASSERT_EQ(3u, s.size());
	ASSERT_EQ(("first"), s[0]);
	ASSERT_EQ(("second"), s[1]);
	ASSERT_EQ(("third"), s[2]);
}

TEST(splitting_tests, split_encoded_string)
{
	std::vector<std::string> s = Uri::split_path(std::string(("heh%2Ffirst/second/third")));
	ASSERT_EQ(3u, s.size());
	ASSERT_EQ(("heh%2Ffirst"), s[0]);
	ASSERT_EQ(("second"), s[1]);
	ASSERT_EQ(("third"), s[2]);
}

TEST(splitting_tests, split_no_slash)
{
	std::vector<std::string> s = Uri::split_path(std::string(("noslash")));
	ASSERT_EQ(1u, s.size());
	ASSERT_EQ(("noslash"), s[0]);
}

TEST(splitting_tests, split_query_basic)
{
	{
		// Separating with '&'
		std::map<std::string, std::string> keyMap =
			Uri::split_query(("key1=value1&key2=value2&key3=value3"));
		ASSERT_EQ(3u, keyMap.size());
		auto iter = keyMap.begin();
		ASSERT_EQ(("key1"), iter->first);
		ASSERT_EQ(("value1"), iter->second);
		++iter;
		ASSERT_EQ(("key2"), iter->first);
		ASSERT_EQ(("value2"), iter->second);
		++iter;
		ASSERT_EQ(("key3"), iter->first);
		ASSERT_EQ(("value3"), iter->second);
	}
	{
		// Separating with ';'
		std::map<std::string, std::string> keyMap =
			Uri::split_query(("key1=value1;key2=value2;key3=value3"));
		ASSERT_EQ(3u, keyMap.size());
		auto iter = keyMap.begin();
		ASSERT_EQ(("key1"), iter->first);
		ASSERT_EQ(("value1"), iter->second);
		++iter;
		ASSERT_EQ(("key2"), iter->first);
		ASSERT_EQ(("value2"), iter->second);
		++iter;
		ASSERT_EQ(("key3"), iter->first);
		ASSERT_EQ(("value3"), iter->second);
	}
}

TEST(splitting_tests, split_encoded_query)
{
	{
		// Separating with '&'
		std::map<std::string, std::string> keyMap =
			Uri::split_query(std::string(("key=value%26key1%20=value1&key2=%5Evalue2&key3=value3%20")));
		ASSERT_EQ(3u, keyMap.size());
		auto iter = keyMap.begin();
		ASSERT_EQ(("key"), iter->first);
		ASSERT_EQ(("value%26key1%20=value1"), iter->second);
		++iter;
		ASSERT_EQ(("key2"), iter->first);
		ASSERT_EQ(("%5Evalue2"), iter->second);
		++iter;
		ASSERT_EQ(("key3"), iter->first);
		ASSERT_EQ(("value3%20"), iter->second);
	}
	{
		// Separating with ';'
		std::map<std::string, std::string> keyMap =
			Uri::split_query(std::string(("key=value%26key1%20=value1;key2=%5Evalue2;key3=value3%20")));
		ASSERT_EQ(3u, keyMap.size());
		auto iter = keyMap.begin();
		ASSERT_EQ(("key"), iter->first);
		ASSERT_EQ(("value%26key1%20=value1"), iter->second);
		++iter;
		ASSERT_EQ(("key2"), iter->first);
		ASSERT_EQ(("%5Evalue2"), iter->second);
		++iter;
		ASSERT_EQ(("key3"), iter->first);
		ASSERT_EQ(("value3%20"), iter->second);
	}
}

TEST(splitting_tests, split_query_empty)
{
	std::map<std::string, std::string> keyMap = Uri::split_query((""));
	ASSERT_EQ(0u, keyMap.size());
}

TEST(splitting_tests, split_query_single)
{
	std::map<std::string, std::string> keyMap = Uri::split_query(("key1=44"));
	ASSERT_EQ(1u, keyMap.size());
	auto iter = keyMap.begin();
	ASSERT_EQ(("key1"), iter->first);
	ASSERT_EQ(("44"), iter->second);
}

TEST(splitting_tests, split_query_no_value)
{
	std::map<std::string, std::string> keyMap = Uri::split_query(("key1"));
	ASSERT_EQ(0u, keyMap.size());
	keyMap = Uri::split_query(("key1="));
	ASSERT_EQ(1u, keyMap.size());
	auto iter = keyMap.begin();
	ASSERT_EQ(("key1"), iter->first);
	ASSERT_EQ((""), iter->second);
	keyMap = Uri::split_query(("key1&"));
	ASSERT_EQ(0u, keyMap.size());
}

TEST(splitting_tests, split_query_no_key)
{
	std::map<std::string, std::string> keyMap = Uri::split_query(("=value1"));
	ASSERT_EQ(1u, keyMap.size());
	auto iter = keyMap.begin();
	ASSERT_EQ((""), iter->first);
	ASSERT_EQ(("value1"), iter->second);
}

TEST(splitting_tests, split_query_end_with_amp)
{
	{
		// Separating with '&'
		std::map<std::string, std::string> keyMap = Uri::split_query(("key1=44&"));
		ASSERT_EQ(1u, keyMap.size());
		auto iter = keyMap.begin();
		ASSERT_EQ(("key1"), iter->first);
		ASSERT_EQ(("44"), iter->second);
	}
	{
		// Separating with ';'
		std::map<std::string, std::string> keyMap = Uri::split_query(("key1=44;"));
		ASSERT_EQ(1u, keyMap.size());
		auto iter = keyMap.begin();
		ASSERT_EQ(("key1"), iter->first);
		ASSERT_EQ(("44"), iter->second);
	}
}

// Helper functions to verify components of a builder.
static void VERIFY_URI_BUILDER(UriBuilder& builder,
	const std::string& scheme,
	const std::string& user_info,
	const std::string& host,
	const int port,
	const std::string& path,
	const std::string& query,
	const std::string& fragment)
{
	ASSERT_EQ(scheme, builder.scheme());
	ASSERT_EQ(host, builder.host());
	ASSERT_EQ(user_info, builder.user_info());
	ASSERT_EQ(port, builder.port());
	ASSERT_EQ(path, builder.path());
	ASSERT_EQ(query, builder.query());
	ASSERT_EQ(fragment, builder.fragment());
}
static void VERIFY_URI_BUILDER(UriBuilder& builder,
	const std::string& scheme,
	const std::string& host,
	const int port)
{
	VERIFY_URI_BUILDER(builder,
		scheme,
		std::string(),
		host,
		port,
		std::string(("/")),
		std::string(),
		std::string());
}
static void VERIFY_URI_BUILDER_IS_EMPTY(UriBuilder& builder)
{
	VERIFY_URI_BUILDER(builder,
		std::string(),
		std::string(),
		std::string(),
		-1,
		std::string(("/")),
		std::string(),
		std::string());
}

TEST(uri_builder_tests, constructor_tests)
{
	// Default constructor
	UriBuilder builder;
	VERIFY_URI_BUILDER_IS_EMPTY(builder);
	// scheme, user_info, host, port
	std::string scheme(("ftp"));
	std::string user_info(("steve:pass"));
	std::string host(("localhost"));
	int port = 44;
	std::string path(("/Yeshere888"));
	std::string uri_str(("ftp://steve:pass@localhost:44/Yeshere888"));

	// std::string
	std::string uri_wstr(("ftp://steve:pass@localhost:44/Yeshere888?abc:123&abc2:456#nose"));
	builder = UriBuilder(uri_wstr);
	VERIFY_URI_BUILDER(builder,
		scheme,
		user_info,
		host,
		port,
		path,
		std::string(("abc:123&abc2:456")),
		std::string(("nose")));

	// copy constructor
	UriBuilder other(builder);
	builder = UriBuilder(uri_str);
	VERIFY_URI_BUILDER(other,
		scheme,
		user_info,
		host,
		port,
		path,
		std::string(("abc:123&abc2:456")),
		std::string(("nose")));
	VERIFY_URI_BUILDER(builder, scheme, user_info, host, port, path, (""), (""));

	// move constructor
	UriBuilder move_other = std::move(builder);
	VERIFY_URI_BUILDER(move_other, scheme, user_info, host, port, path, (""), (""));
}

TEST(uri_builder_tests, assignment_operators)
{
	// assignment operator
	const std::string scheme = ("http"), host = ("localhost");
	const int port = 44;
	UriBuilder original;
	original.set_scheme(scheme).set_host(host).set_port(port);
	UriBuilder assign;
	assign = original;
	VERIFY_URI_BUILDER(assign, scheme, std::string(host), port);

	// move assignment operator
	UriBuilder move_assign;
	move_assign = std::move(original);
	VERIFY_URI_BUILDER(assign, scheme, std::string(host), port);
}

TEST(uri_builder_tests, set_port_as_string)
{
	UriBuilder builder;

	ASSERT_THROW(builder.set_port(("")), std::invalid_argument);
	ASSERT_EQ(-1, builder.port());

	builder.set_port(("987"));
	ASSERT_EQ(987, builder.port());

	ASSERT_THROW(builder.set_port(("abc")), std::invalid_argument);
	ASSERT_EQ(987, builder.port());

	builder.set_port((" 44 "));
	ASSERT_EQ(44, builder.port());

	builder.set_port(("99"));
	ASSERT_EQ(99, builder.port());
}

TEST(uri_builder_tests, component_assignment)
{
	UriBuilder builder;
	const std::string scheme(("myscheme"));
	const std::string uinfo(("johndoe:test"));
	const std::string host(("localhost"));
	const int port = 88;
	const std::string path(("jklajsd"));
	const std::string query(("key1=val1"));
	const std::string fragment(("last"));

	builder.set_scheme(scheme);
	builder.set_user_info(uinfo);
	builder.set_host(host);
	builder.set_port(port);
	builder.set_path(path);
	builder.set_query(query);
	builder.set_fragment(fragment);

	VERIFY_URI_BUILDER(builder, scheme, uinfo, host, port, path, query, fragment);
}

TEST(uri_builder_tests, component_assignment_encode)
{
	{
		UriBuilder builder;
		const std::string scheme(("myscheme"));
		const std::string uinfo(("johndoe:test"));
		const std::string host(("localhost"));
		const int port = 88;
		const std::string path(("jklajsd/yes no"));
		const std::string query(("key1=va%l1"));
		const std::string fragment(("las t"));

		builder.set_scheme(scheme);
		builder.set_user_info(uinfo, true);
		builder.set_host(host, true);
		builder.set_port(port);
		builder.set_path(path, true);
		builder.set_query(query, true);
		builder.set_fragment(fragment, true);

		VERIFY_URI_BUILDER(builder,
			scheme,
			std::string(("johndoe:test")),
			std::string(("localhost")),
			port,
			std::string(("jklajsd/yes%20no")),
			std::string(("key1=va%25l1")),
			std::string(("las%20t")));
	}
	{
		UriBuilder builder;
		const std::string scheme(("myscheme"));
		const std::string uinfo(("johndoe:test"));
		const std::string host(("localhost"));
		const int port = 88;
		const std::string path(("jklajsd/yes no"));
		const std::string query(("key1=va%l1"));
		const std::string fragment(("las t"));

		builder.set_scheme(scheme);
		builder.set_user_info(uinfo, true);
		builder.set_host(host, true);
		builder.set_port(port);
		builder.set_path(path, true);
		builder.set_query(query, true);
		builder.set_fragment(fragment, true);

		VERIFY_URI_BUILDER(builder,
			scheme,
			std::string(("johndoe:test")),
			std::string(("localhost")),
			port,
			std::string(("jklajsd/yes%20no")),
			std::string(("key1=va%25l1")),
			std::string(("las%20t")));
	}
}

TEST(uri_builder_tests, validation)
{
	{
		// true
		UriBuilder builder(("http://localhost:4567/"));
		ASSERT_TRUE(builder.is_valid());

		// false
		builder = UriBuilder();
		builder.set_scheme(("123"));
		ASSERT_FALSE(builder.is_valid());
	}
	{
		// true
		UriBuilder builder(("http://localhost:4567/"));
		ASSERT_TRUE(builder.is_valid());

		// false
		builder = UriBuilder();
		builder.set_scheme(("123"));
		ASSERT_FALSE(builder.is_valid());
	}
}

TEST(uri_builder_tests, uri_creation_string)
{
	std::string uri_str(("http://steve:temp@localhost:4556/"));

	// to_string
	UriBuilder builder(uri_str);
	ASSERT_EQ(uri_str, builder.to_string());

	// to_string
	ASSERT_EQ(uri_str, builder.to_string());

	// to Uri
	ASSERT_EQ(uri_str, builder.to_uri().to_string());

	// to encoded string
	UriBuilder with_space(builder);
	with_space.set_path(std::string(("path%20with%20space")));
	ASSERT_EQ(("http://steve:temp@localhost:4556/path%20with%20space"), with_space.to_string());
}

TEST(uri_builder_tests, append_path_string)
{
	// empty Uri builder path
	UriBuilder builder;
	builder.append_path(("/path1"));
	ASSERT_EQ(("/path1"), builder.path());

	// empty append path
	builder.append_path((""));
	ASSERT_EQ(("/path1"), builder.path());

	// Uri builder with slash
	builder.append_path(("/"));
	builder.append_path(("path2"));
	ASSERT_EQ(("/path1/path2"), builder.path());

	// both with slash
	builder.append_path(("/"));
	builder.append_path(("/path3"));
	ASSERT_EQ(("/path1/path2/path3"), builder.path());

	// both without slash
	builder.append_path(("path4"));
	ASSERT_EQ(("/path1/path2/path3/path4"), builder.path());

	// encoding
	builder.clear();
	builder.append_path(("encode%things"));
	ASSERT_EQ(("/encode%things"), builder.path());

	builder.clear();
	builder.append_path(("encode%things"), false);
	ASSERT_EQ(("/encode%things"), builder.path());

	builder.clear();
	builder.append_path(("encode%things"), true);
	ASSERT_EQ(("/encode%25things"), builder.path());

	// self references
	builder.set_path(("example"));
	builder.append_path(builder.path());
	ASSERT_EQ(("example/example"), builder.path());

	builder.set_path(("/example"));
	builder.append_path(builder.path());
	ASSERT_EQ(("/example/example"), builder.path());

	builder.set_path(("/example/"));
	builder.append_path(builder.path());
	ASSERT_EQ(("/example/example/"), builder.path());
}

TEST(uri_builder_tests, append_path_raw_string)
{
	// empty Uri builder path
	UriBuilder builder;
	builder.append_path_raw(("path1"));
	ASSERT_EQ(("/path1"), builder.path());

	// empty append path
	builder.append_path_raw((""));
	ASSERT_EQ(("/path1"), builder.path());

	// Uri builder with slash
	builder.append_path_raw(("/"));
	builder.append_path_raw(("path2"));
	ASSERT_EQ(("/path1///path2"), builder.path());

	// leading slash (should result in "//")
	builder.append_path_raw(("/path3"));
	ASSERT_EQ(("/path1///path2//path3"), builder.path());

	// trailing slash
	builder.append_path_raw(("path4/"));
	builder.append_path_raw(("path5"));
	ASSERT_EQ(("/path1///path2//path3/path4//path5"), builder.path());

	// encoding
	builder.clear();
	builder.append_path_raw(("encode%things"));
	ASSERT_EQ(("/encode%things"), builder.path());

	builder.clear();
	builder.append_path_raw(("encode%things"), false);
	ASSERT_EQ(("/encode%things"), builder.path());

	builder.clear();
	builder.append_path_raw(("encode%things"), true);
	ASSERT_EQ(("/encode%25things"), builder.path());

	// self references
	builder.set_path(("example"));
	builder.append_path_raw(builder.path());
	ASSERT_EQ(("example/example"), builder.path());

	builder.set_path(("/example"));
	builder.append_path_raw(builder.path());
	ASSERT_EQ(("/example//example"), builder.path());

	builder.set_path(("/example/"));
	builder.append_path_raw(builder.path());
	ASSERT_EQ(("/example///example/"), builder.path());
}

TEST(uri_builder_tests, append_query_string)
{
	// empty Uri builder query
	UriBuilder builder;
	builder.append_query(("key1=value1"));
	ASSERT_EQ(("key1=value1"), builder.query());

	// empty append query
	builder.append_query((""));
	ASSERT_EQ(("key1=value1"), builder.query());

	// Uri builder with ampersand
	builder.append_query(("&"));
	builder.append_query(("key2=value2"));
	ASSERT_EQ(("key1=value1&key2=value2"), builder.query());

	// both with ampersand
	builder.append_query(("&"));
	builder.append_query(("&key3=value3"));
	ASSERT_EQ(("key1=value1&key2=value2&key3=value3"), builder.query());

	// both without ampersand
	builder.append_query(("key4=value4"));
	ASSERT_EQ(("key1=value1&key2=value2&key3=value3&key4=value4"), builder.query());

	// number query
	builder.append_query("key5", 1);
	ASSERT_EQ(("key1=value1&key2=value2&key3=value3&key4=value4&key5=1"), builder.query());

	// string query
	builder.append_query(("key6"), ("val6"));
	ASSERT_EQ(("key1=value1&key2=value2&key3=value3&key4=value4&key5=1&key6=val6"), builder.query());

	// key and value separate with '=', '&', and ';'
	builder.append_query(("key=&;"), ("=&;value"));
	ASSERT_EQ(
		("key1=value1&key2=value2&key3=value3&key4=value4&key5=1&key6=val6&key%3D%26%3B=%3D%26%3Bvalue"),
		builder.query());

	// self references
	builder.set_query(("example"));
	builder.append_query(builder.query());
	ASSERT_EQ(("example&example"), builder.query());

	builder.set_query(("&example"));
	builder.append_query(builder.query());
	ASSERT_EQ(("&example&example"), builder.query());

	builder.set_query(("&example&"));
	builder.append_query(builder.query());
	ASSERT_EQ(("&example&example&"), builder.query());
}

TEST(uri_builder_tests, append_query_string_no_encode)
{
	UriBuilder builder;
	builder.append_query(("key=&;"), ("=&;value"), false);
	ASSERT_EQ(("key=&;==&;value"), builder.query());
}

TEST(uri_builder_tests, append_string)
{
	// with just path
	UriBuilder builder;
	builder.append(Uri("/path1"));
	ASSERT_EQ(("/path1"), builder.path());

	// with just query
	builder.append(Uri("?key1=value1"));
	ASSERT_EQ(("/path1"), builder.path());
	ASSERT_EQ(("key1=value1"), builder.query());
	ASSERT_EQ(("/path1?key1=value1"), builder.to_string());

	// with just fragment
	builder.append(Uri("#fragment"));
	ASSERT_EQ(("/path1"), builder.path());
	ASSERT_EQ(("key1=value1"), builder.query());
	ASSERT_EQ(("fragment"), builder.fragment());
	ASSERT_EQ(("/path1?key1=value1#fragment"), builder.to_string());

	// with all
	builder.append(Uri("/path2?key2=value2#frag2"));
	ASSERT_EQ(("/path1/path2"), builder.path());
	ASSERT_EQ(("key1=value1&key2=value2"), builder.query());
	ASSERT_EQ(("fragmentfrag2"), builder.fragment());
	ASSERT_EQ(("/path1/path2?key1=value1&key2=value2#fragmentfrag2"), builder.to_string());
}

TEST(uri_builder_tests, append_empty_string)
{
	std::string uri_str(("http://uribuilder.com/"));
	UriBuilder builder(uri_str);
	builder.append(Uri(""));

	ASSERT_EQ(builder.to_string(), uri_str);
}

TEST(uri_builder_tests, append_path_encoding)
{
	UriBuilder builder;
	builder.append_path(("/path space"), true);
	ASSERT_EQ(("/path%20space"), builder.path());

	builder.append_path(("path2"));
	ASSERT_EQ(("/path%20space/path2"), builder.path());
}

TEST(uri_builder_tests, append_query_encoding)
{
	UriBuilder builder;
	builder.append_query(("key1 =value2"), true);
	ASSERT_EQ(("key1%20=value2"), builder.query());

	builder.append_query(("key2=value3"));
	ASSERT_EQ(("key1%20=value2&key2=value3"), builder.query());
}

TEST(uri_builder_tests, append_encoding)
{
	UriBuilder builder;
	builder.append(Uri(Uri::encode_uri(("path space?key =space#frag space"))));
	ASSERT_EQ(("/path%20space"), builder.path());
	ASSERT_EQ(("key%20=space"), builder.query());
	ASSERT_EQ(("frag%20space"), builder.fragment());
	ASSERT_EQ(("/path%20space?key%20=space#frag%20space"), builder.to_string());

	// try with encoded_string
	builder = UriBuilder();
	builder.append(Uri("/path2?key2=value2#frag2"));
	ASSERT_EQ(("/path2"), builder.path());
	ASSERT_EQ(("key2=value2"), builder.query());
	ASSERT_EQ(("frag2"), builder.fragment());
	ASSERT_EQ(("/path2?key2=value2#frag2"), builder.to_string());
}

TEST(uri_builder_tests, host_encoding)
{
	// Check that ASCII characters that are invalid in a host name
	// do not get percent-encoded.

	UriBuilder ub1;
	ub1.set_scheme(("http")).set_host(("????dfasddsf!@#$%^&*()_+")).set_port(80);

	ASSERT_FALSE(ub1.is_valid());
}

TEST(uri_builder_tests, clear)
{
	UriBuilder ub;
	ub.clear();
	EXPECT_TRUE(ub.scheme() == (""));
	EXPECT_TRUE(ub.user_info() == (""));
	EXPECT_TRUE(ub.host() == (""));
	EXPECT_TRUE(ub.port() == -1);
	EXPECT_TRUE(ub.path() == ("/"));
	EXPECT_TRUE(ub.query() == (""));
	EXPECT_TRUE(ub.fragment() == (""));

	ub = UriBuilder(("http://myhost.com/path1"));
	ub.append_path(("path2"));
	Uri u = ub.to_uri();
	ub.clear();
	EXPECT_TRUE(ub.scheme() == (""));
	EXPECT_TRUE(ub.user_info() == (""));
	EXPECT_TRUE(ub.host() == (""));
	EXPECT_TRUE(ub.port() == -1);
	EXPECT_TRUE(ub.path() == ("/"));
	EXPECT_TRUE(ub.query() == (""));
	EXPECT_TRUE(ub.fragment() == (""));
	EXPECT_TRUE(u.to_string() == ("http://myhost.com/path1/path2"));

	ub.append_path(("path3"));
	ub.set_host(("hahah"));
	ub.set_fragment(("No"));
	ub.clear();
	EXPECT_TRUE(ub.scheme() == (""));
	EXPECT_TRUE(ub.user_info() == (""));
	EXPECT_TRUE(ub.host() == (""));
	EXPECT_TRUE(ub.port() == -1);
	EXPECT_TRUE(ub.path() == ("/"));
	EXPECT_TRUE(ub.query() == (""));
	EXPECT_TRUE(ub.fragment() == (""));
}

TEST(uri_builder_tests, to_string_invalid_uri)
{
	UriBuilder builder(("http://invaliduri.com"));
	builder.set_scheme(("1http"));
	ASSERT_THROW(builder.to_string(), UriException);
	ASSERT_THROW(builder.to_uri(), UriException);

	builder.set_scheme(("ht*ip"));
	ASSERT_THROW(builder.to_string(), UriException);

	builder.set_scheme(("htt%20p"));
	ASSERT_THROW(builder.to_string(), UriException);
}
